package {
	/**
	* Defformation with Heightmap
	* @author Exey Panteleev
	*/
	
	import flash.display.MovieClip;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.geom.Matrix;
	import org.papervision3d.core.utils.Mouse3D;
	import org.papervision3d.core.utils.InteractiveSceneManager;
	import org.papervision3d.core.utils.virtualmouse.VirtualMouse;
	import org.papervision3d.core.geom.renderables.Triangle3D;
	import org.papervision3d.core.geom.renderables.Vertex3D;
	import org.papervision3d.materials.MovieMaterial;
	import org.papervision3d.materials.ColorMaterial;
	import org.papervision3d.materials.utils.MaterialsList;
	import org.papervision3d.objects.primitives.Cone;
	import org.papervision3d.objects.primitives.Cube;
	import org.papervision3d.objects.primitives.Sphere;
	import org.papervision3d.view.BasicView;
	import org.papervision3d.events.InteractiveScene3DEvent;
	import flash.events.Event;	

	public class HeightmapDrawing extends BasicView {
		
		// ratio TESSELATION_W/TESSELATION_H must coresspond with movieclip canvas
		const TESSELATION_W:uint = 12;
		const TESSELATION_H:uint = 8;
		const RATIO_X:Number = TESSELATION_W / 240; // canvas.width
		const RATIO_Y:Number = TESSELATION_H / 160; // canvas.height
		
		var mouse3D:Mouse3D;
		var mouseVirtual:VirtualMouse;
		
		var sculptBrush:Cube;

		var heightMapMat:MovieMaterial;
		var heightMapBitmapData:BitmapData = new BitmapData(TESSELATION_W, TESSELATION_H);
		var bitmapHeightMapPreviewer:Bitmap = new Bitmap(heightMapBitmapData);
		
		var sculptThing:Sphere;
		var sculptThingRef:Sphere;
		
		var surface:MovieClip;
		
		
	function HeightmapDrawing(){
		super(640, 480, true, true, "CAMERA3D");
		preparePv3d();
		createMaterials();
		createObjects();
		addListeners();
	}

	function preparePv3d() {
		mouseVirtual = viewport.interactiveSceneManager.virtualMouse;
		mouse3D = viewport.interactiveSceneManager.mouse3D;
		Mouse3D.enabled = true;
		camera.zoom = 5;
	}
	function createMaterials() {
		
		heightMapMat = new MovieMaterial(new canvas());
		heightMapMat.interactive = true;
		heightMapMat.animated = true;
		heightMapMat.smooth = true;
		heightMapMat.allowAutoResize = false;
		//heightMapMat.precise = true;
		heightMapMat.lineAlpha = 0;
		
		surface = heightMapMat.movie["surface"];
		surface.visible = false;
		
		// heightmap preview
		bitmapHeightMapPreviewer.scaleX = bitmapHeightMapPreviewer.scaleY = 4
		bitmapHeightMapPreviewer.y = 40;
		bitmapHeightMapPreviewer.x = 560;
		stage.addChild(bitmapHeightMapPreviewer);
	}
	function createObjects() {
		sculptBrush = new Cube(new MaterialsList( { all: new ColorMaterial(0xff0000) } ), 10, 10, 10);
		//sculptBrush = new Cone(new ColorMaterial(0xff0000), 10, 10, 2, 3);
		scene.addChild(sculptBrush);
		
		sculptThing = new Sphere(heightMapMat, 480, TESSELATION_W, TESSELATION_H)
		sculptThing.z = 480;
		sculptThing.rotationY = -90;
		scene.addChild(sculptThing);		
		sculptThingRef = new Sphere(heightMapMat, 480, TESSELATION_W, TESSELATION_H)
	}
	function addListeners() {
		addEventListener(Event.ENTER_FRAME, onEnterFrame);
		sculptThing.addEventListener(InteractiveScene3DEvent.OBJECT_MOVE, handleMouseMove);
		sculptThing.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, handleMousePress);
	}
	
	function onEnterFrame(event:Event) {
		sculptThing.yaw(.5);
		singleRender();
		updateGeometry();
	}
	
	function handleMouseMove(event:InteractiveScene3DEvent) {
		if (InteractiveSceneManager.MOUSE_IS_DOWN) {
			strokeBrush()
		}
		if (sculptBrush != null) {
			sculptBrush.copyTransform(mouse3D);
		}
	}
	function handleMousePress(event:InteractiveScene3DEvent) {
		strokeBrush()
	}
	function strokeBrush() {
		surface.graphics.beginFill(0x000000, 1)
		//surface.graphics.drawCircle(mouseVirtual.x, mouseVirtual.y, 5)
		surface.graphics.drawRect(mouseVirtual.x-10, mouseVirtual.y-10, 20, 20);
		surface.graphics.endFill();		
	}
	function updateGeometry() {
	
		var scaleMatrix:Matrix = new Matrix();
		scaleMatrix.scale(RATIO_X, RATIO_Y); 
		heightMapBitmapData.draw(surface, scaleMatrix);
		bitmapHeightMapPreviewer = new Bitmap(heightMapBitmapData);

		unbulgingVertex(0, TESSELATION_W/2, TESSELATION_H-1) // bottom vertex (hack! because need additional line on height map?)
		var vertexIndex = 1;
			for (var iy:int = TESSELATION_H-1; iy > 0; iy--) {	
				for (var ix:int = 0; ix <= TESSELATION_W-1; ix++) {
					unbulgingVertex(vertexIndex, ix, iy)
					vertexIndex++;
			}
		}	
		unbulgingVertex(sculptThing.geometry.vertices.length-1, TESSELATION_W/2, 0) // top vertex 
		
		for each(var face:Triangle3D in sculptThing.geometry.faces)
			face.createNormal();
		
		for each(var vertex:Vertex3D in sculptThing.geometry.vertices)
			vertex.calculateNormal();	
	}
	
	function unbulgingVertex(vertexIndex:uint, pixelX:uint, pixelY:uint) {
		var destPoint:Number = Math.max(heightMapBitmapData.getPixel(pixelX, pixelY) * .00000001 + .6, 0);
		sculptThing.geometry.vertices[vertexIndex].x = sculptThingRef.geometry.vertices[vertexIndex].x * destPoint;
		sculptThing.geometry.vertices[vertexIndex].y = sculptThingRef.geometry.vertices[vertexIndex].y * destPoint;
		sculptThing.geometry.vertices[vertexIndex].z = sculptThingRef.geometry.vertices[vertexIndex].z * destPoint;
		//trace(vertexIndex,"("+int(sculptThing.geometry.vertices[vertexIndex].x)+", "+int(sculptThing.geometry.vertices[vertexIndex].y)+", "+int(sculptThing.geometry.vertices[vertexIndex].z)+")"," x:", pixelX," y:", pixelY," getPixel:", heightMapBitmapData.getPixel(pixelX, pixelY), " destPoint: ", destPoint)
	}
}
}